/*
 * IMNetworkStorageM.nc -- network storage component
 *
 * Copyright (C) Andrey Vihrov <andrey.vihrov@gmail.com>, 2010
 * Copyright (C) Askar Salimbaev <ghostinshell@gmail.com>, 2010
 *
 * This file is part of Intermote.
 *
 * Intermote is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Intermote is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Intermote.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <Intermote.h>

module IMNetworkStorageM {

	provides {
		interface IMBlockStorage as BlockStorage;
		interface IMBlockProperty as BlockProperty[uint8_t];
	}

	uses {
		interface IMBlockId as BlockId;

		interface Packet as RTPacket;
		interface AMPacket as RTAMPacket;

		interface Packet as FLPacket;

		interface AMSend as AllocateSend;
		interface Receive as AllocateReply;
		interface IMTimeout as AllocateTimeout;
		interface Send as AllocateFloodSend;

		interface AMSend as ReadSend;
		interface Receive as ReadReply;
		interface IMTimeout as ReadTimeout;

		interface AMSend as WriteSend;
		interface Receive as WriteReply;
		interface IMTimeout as WriteTimeout;

		interface AMSend as GetPropertySend;
		interface Receive as GetPropertyReply;
		interface IMTimeout as GetPropertyTimeout;

		interface AMSend as SetPropertySend;
		interface Receive as SetPropertyReply;
		interface IMTimeout as SetPropertyTimeout;

		interface AMSend as EraseSend;

		interface Send as ReserveSend;
	}

}
implementation {

	message_t packet;

	/* BlockStorage interface operations */

	/* The node holding last allocated block */
	am_addr_t last_alloc = AM_BROADCAST_ADDR;

	error_t allocate_flood (void)
	{
		error_t res;

		call FLPacket.clear(&packet);
		call FLPacket.setPayloadLength(&packet, 0);

		res = call AllocateFloodSend.send(&packet, 0);
		if (res == SUCCESS)
		{
			call AllocateTimeout.stop();
			call AllocateTimeout.start();
		}

		return res;
	}

	event void AllocateFloodSend.sendDone (message_t *msg, error_t err)
	{
		if (msg == &packet && err != SUCCESS)
		{
			call AllocateTimeout.stop();
			signal BlockStorage.allocateDone(err, 0);
		}
	}

	command	error_t BlockStorage.allocate (void)
	{
			if (last_alloc == AM_BROADCAST_ADDR)
			return allocate_flood();
		else
		{
			error_t err;

			call RTPacket.clear(&packet);
			call RTPacket.setPayloadLength(&packet, 0);

			err = call AllocateSend.send(last_alloc, &packet, 0);
			if (err == SUCCESS)
				call AllocateTimeout.start();

			return err;
		}
	}

	event void AllocateSend.sendDone (message_t *msg, error_t err)
	{
		if (msg == &packet && err != SUCCESS)
		{
			call AllocateTimeout.stop();
			signal BlockStorage.allocateDone(err, 0);
		}
	}

	event void AllocateTimeout.timeout (void)
	{
		if (last_alloc == AM_BROADCAST_ADDR)
			signal BlockStorage.allocateDone(FAIL, 0);
		else
		{
			last_alloc = AM_BROADCAST_ADDR;
			if (allocate_flood() != SUCCESS)
				signal BlockStorage.allocateDone(FAIL, 0);
		}
	}

	event message_t *AllocateReply.receive (message_t *msg, void *payload, uint8_t len)
	{
		const allocate_reply_t *data = payload;

		if (len == sizeof(allocate_reply_t))
		{
			call AllocateTimeout.stop();
			
			if (data->status != SUCCESS)
			{
				last_alloc = AM_BROADCAST_ADDR;
				if (allocate_flood() != SUCCESS)
					signal BlockStorage.allocateDone(FAIL, 0);
			}
			else
			{
				last_alloc = call RTAMPacket.source(msg);
				signal BlockStorage.allocateDone(data->status, data->block);
			}
		}

		return msg;
	}

	/**********************************************************************/

	command error_t BlockStorage.erase (im_block_id_t block)
	{
		erase_request_t *data;

		call RTPacket.clear(&packet);
		data = call RTPacket.getPayload(&packet, sizeof(erase_request_t));
		if (!data)
			return FAIL;
		data->block = block;

		return call EraseSend.send(call BlockId.node(block), &packet, sizeof(erase_request_t));
	}

	event void EraseSend.sendDone (message_t *msg, error_t err)
	{
		if (msg == &packet)
			signal BlockStorage.eraseDone(err);
	}

	/**********************************************************************/

	struct im_chunk *read_buf;

	command error_t BlockStorage.read (im_block_id_t block, im_off_t offset, struct im_chunk *buf)
	{
		read_request_t *data;
		error_t err;

		read_buf = buf;

		call RTPacket.clear(&packet);
		data = call RTPacket.getPayload(&packet, sizeof(read_request_t));
		if (!data)
			return FAIL;
		data->block = block;
		data->offset = offset;

		err = call ReadSend.send(call BlockId.node(block), &packet, sizeof(read_request_t));
		if (err == SUCCESS)
			call ReadTimeout.start();

		return err;
	}

	event void ReadSend.sendDone (message_t *msg, error_t err)
	{
		if (msg == &packet && err != SUCCESS)
		{
			call ReadTimeout.stop();
			signal BlockStorage.readDone(FAIL);
		}
	}

	event void ReadTimeout.timeout (void)
	{
		signal BlockStorage.readDone(FAIL);
	}

	event message_t *ReadReply.receive (message_t *msg, void *payload, uint8_t len)
	{
		const read_reply_t *data = payload;

		if (len == sizeof(read_reply_t))
		{
			call ReadTimeout.stop();
			memcpy(read_buf->data, data->data, IM_CHUNK_SIZE);
			signal BlockStorage.readDone(SUCCESS);
		}

		return msg;
	}

	/**********************************************************************/

	command error_t BlockStorage.write (im_block_id_t block, im_off_t offset, const struct im_chunk *buf)
	{
		write_request_t *data;
		error_t err;

		call RTPacket.clear(&packet);
		data = call RTPacket.getPayload(&packet, sizeof(write_request_t));
		if (!data)
			return FAIL;
		data->block = block;
		data->offset = offset;
		memcpy(data->data, buf->data, IM_CHUNK_SIZE);

		err = call WriteSend.send(call BlockId.node(block), &packet, sizeof(write_request_t));
		if (err == SUCCESS)
			call WriteTimeout.start();

		return err;
	}

	event void WriteSend.sendDone (message_t *msg, error_t err)
	{
		if (msg == &packet && err != SUCCESS)
		{
			call WriteTimeout.stop();
			signal BlockStorage.writeDone(err);
		}
	}

	event void WriteTimeout.timeout (void)
	{
		signal BlockStorage.writeDone(FAIL);
	}

	event message_t *WriteReply.receive (message_t *msg, void *payload, uint8_t len)
	{
		if (len == 0)
		{
			call WriteTimeout.stop();
			signal BlockStorage.writeDone(SUCCESS);
		}

		return msg;
	}

	/**********************************************************************/

	command error_t BlockStorage.reserve (uint8_t count)
	{
		reserve_request_t *data;

		call FLPacket.clear(&packet);
		data = call FLPacket.getPayload(&packet, sizeof(reserve_request_t));
		if (!data)
			return FAIL;
		data->count = count;

		return call ReserveSend.send(&packet, sizeof(reserve_request_t));
	}

	event void ReserveSend.sendDone (message_t *msg, error_t err) { }

	/**********************************************************************/


	/* BlockProperty interface operations */

	uint8_t cur_prop;


	command error_t BlockProperty.set[uint8_t prop] (im_block_id_t block, uint32_t propdata)
	{
		set_property_request_t *data;
		error_t err;

		cur_prop = prop;

		call RTPacket.clear(&packet);
		data = call RTPacket.getPayload(&packet, sizeof(set_property_request_t));
		if (!data)
			return FAIL;
		data->property = prop;
		data->block = block;
		data->data = propdata;

		err = call SetPropertySend.send(call BlockId.node(block), &packet, sizeof(set_property_request_t));
		if (err == SUCCESS)
			call SetPropertyTimeout.start();

		return err;
	}

	event void SetPropertySend.sendDone (message_t *msg, error_t err)
	{
		if (msg == &packet && err != SUCCESS)
		{
			call SetPropertyTimeout.stop();
			signal BlockProperty.setDone[cur_prop](err);
		}
	}

	event void SetPropertyTimeout.timeout (void)
	{
		signal BlockProperty.setDone[cur_prop](FAIL);
	}

	event message_t *SetPropertyReply.receive (message_t *msg, void *payload, uint8_t len)
	{
		if (len == 0)
		{
			call SetPropertyTimeout.stop();
			signal BlockProperty.setDone[cur_prop](SUCCESS);
		}

		return msg;
	}

	/**********************************************************************/

	command error_t BlockProperty.get[uint8_t prop] (im_block_id_t block)
	{
		get_property_request_t *data;
		error_t err;

		cur_prop = prop;

		call RTPacket.clear(&packet);
		data = call RTPacket.getPayload(&packet, sizeof(get_property_request_t));
		if (!data)
			return FAIL;
		data->property = prop;
		data->block = block;

		err = call GetPropertySend.send(call BlockId.node(block), &packet, sizeof(get_property_request_t));
		if (err == SUCCESS)
			call GetPropertyTimeout.start();

		return err;
	}

	event void GetPropertySend.sendDone (message_t *msg, error_t err)
	{
		if (msg == &packet && err != SUCCESS)
		{
			call GetPropertyTimeout.stop();
			signal BlockProperty.getDone[cur_prop](err, 0);
		}
	}

	event void GetPropertyTimeout.timeout (void)
	{
		signal BlockProperty.getDone[cur_prop](FAIL, 0);
	}

	event message_t *GetPropertyReply.receive (message_t *msg, void *payload, uint8_t len)
	{
		const get_property_reply_t *data = payload;

		if (len == sizeof(get_property_reply_t))
		{
			call GetPropertyTimeout.stop();
			signal BlockProperty.getDone[cur_prop](SUCCESS, data->data);
		}

		return msg;
	}

	/**********************************************************************/

	default event void BlockProperty.setDone[uint8_t prop] (error_t err)
	{

	}

	default event void BlockProperty.getDone[uint8_t prop] (error_t err, uint32_t data)
	{

	}

}
